package com.github.laozhaishaozuo.asm;

import java.io.FileOutputStream;
import java.lang.reflect.Method;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class HelloAsm extends ClassLoader implements Opcodes {

	public static void main(final String args[]) throws Exception {


		// 定义一个叫做Example的类
		ClassWriter cw = new ClassWriter(0);
		cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);

		// 生成默认的构造方法
		MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);

		// 生成构造方法的字节码指令
		mw.visitVarInsn(ALOAD, 0);
		mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
		mw.visitInsn(RETURN);
		mw.visitMaxs(1, 1);
		mw.visitEnd();

		// 生成main方法
		mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);

		// 生成main方法中的字节码指令
		mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");

		mw.visitLdcInsn("Hello world!");
		mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

		mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		mw.visitInsn(ICONST_1);
		mw.visitInsn(ICONST_2);
		mw.visitMethodInsn(INVOKESTATIC, "Example", "add", "(II)I", false);
		mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);

		mw.visitInsn(RETURN);
		mw.visitMaxs(4, 2);

		// 字节码生成完成
		mw.visitEnd();

		mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "add", "(II)I", null, null);
		mw.visitVarInsn(ILOAD, 0);
		mw.visitVarInsn(ILOAD, 1);
		mw.visitInsn(IADD);
		mw.visitInsn(IRETURN);
		mw.visitMaxs(2, 3);
		// 字节码生成完成
		mw.visitEnd();

		// 获取生成的class文件对应的二进制流
		byte[] code = cw.toByteArray();


		// 将二进制流写到本地磁盘上
		FileOutputStream fos = new FileOutputStream("Example.class");
		fos.write(code);
		fos.close();

		// 直接将二进制流加载到内存中
		HelloAsm loader = new HelloAsm();
		Class<?> exampleClass = loader.defineClass("Example", code, 0, code.length);
		System.out.println(exampleClass.getName());

		// 通过反射调用main方法
		Method[] methods = exampleClass.getMethods();
		Method method = null;
		for (int i = 0; i < methods.length; i++) {
			method = methods[i];
			if ("main".equals(method.getName())) {
				method.invoke(null, new Object[]{null});
			} else if ("add".equals(method.getName())) {
				System.out.println(method.invoke(null, new Object[]{45, 45}));
			}
		}

	}

	private static void displayMethodInfo(Method method) {
		System.out.println("method : " + method.getName());
		Class<?>[] types = method.getParameterTypes();
		System.out.print("parameter types : ");
		for (int j = 0; j < types.length; j++) {
			System.out.print(types[j].getName() + " ");
		}
		System.out.println();
	}
}
